package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/bitly/go-simplejson"
)

type PromData struct {
	IP        string
	SubMetric string
	Metric    string
	Tags      map[string]interface{}
	Timestamp int64
	Value     string
}

func Scrape(promUrl string) ([]PromData, error) {
	resp, err := http.Get(promUrl)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return Parse(body)
}

func Parse(text []byte) ([]PromData, error) {
	rs := make([]PromData, 0)
	js, err := simplejson.NewJson(text)
	if err != nil {
		return rs, err
	}
	switch js.Get("status").MustString() {
	case "error":
		var errType, errText string
		if errType, err = js.Get("errorType").String(); err != nil {
			return rs, fmt.Errorf("parse prometheus data error: %v", err)
		}
		if errText, err = js.Get("error").String(); err != nil {
			return rs, fmt.Errorf("parse prometheus data error: %v", err)
		}
		return rs, fmt.Errorf("prometheus: errorType:%s, error:%s", errType, errText)
	case "success":
		resultType, err := js.Get("data").Get("resultType").String()
		if err != nil {
			return rs, err
		}
		if resultType != "vector" {
			return rs, fmt.Errorf("unsolved resultType: %s", resultType)
		}
		results, err := js.Get("data").Get("result").Array()
		if err != nil {
			return rs, err
		}

		for i, _ := range results {
			data := js.Get("data").Get("result").GetIndex(i)
			tags := data.Get("metric").MustMap()
			timestamp := data.Get("value").GetIndex(0).MustFloat64()
			value := data.Get("value").GetIndex(1).MustString()

			subMetric := ""
			if tags["m"] != nil {
				subMetric = tags["m"].(string)
			} else if tags["status"] != nil {
				subMetric = tags["status"].(string)
			}
			ip := ""
			if tags["ip"] != nil {
				ip = tags["ip"].(string)
			} else if tags["host"] != nil {
				ip = tags["host"].(string)
			} else {
				return rs, fmt.Errorf("parse prometheus data: no ip tag")
			}

			pd := PromData{
				IP:        ip,
				Metric:    tags["__name__"].(string),
				SubMetric: subMetric,
				Tags:      tags,
				Timestamp: int64(timestamp),
				Value:     value,
			}

			rs = append(rs, pd)
		}

	}
	return rs, nil
}

func MetricMapping(pd PromData, mapper Mapper) (string, error) {
	if mapper[pd.Metric][pd.SubMetric] == "" {
		return "", fmt.Errorf("MetricMapping: no matching metric of %s.%s", pd.Metric, pd.SubMetric)
	}
	if pd.Metric == "disk_usage" {
		disk := pd.Tags["path"].(string)
		diskBase64 := base64.StdEncoding.EncodeToString([]byte(disk))
		return fmt.Sprintf(mapper[pd.Metric][pd.SubMetric], diskBase64), nil
	} else {
		return mapper[pd.Metric][pd.SubMetric], nil
	}
}

func Transform(pd PromData, mapper Mapper) (string, error) {
	metric, err := MetricMapping(pd, mapper)
	if err != nil {
		return "", err
	}
	ip := strings.Replace(pd.IP, ".", "_", -1)
	return fmt.Sprintf("golia.%s.%s %s %v",
		ip, metric, pd.Value, pd.Timestamp), nil
}
